import java.util.ArrayList;
import java.util.Random;

import org.nlogo.headless.HeadlessWorkspace;


public class RecuitSimuleBis {

	public static void main(String[] args) {

		HeadlessWorkspace ws = HeadlessWorkspace.newInstance();
		int NBNOEUDS = 200;
		try {
			ws.open("projet.nlogo");
			ws.command("set n " + (NBNOEUDS - 3));
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		Random r = new Random();
		System.out.print("création des solutions aléatoires... ");
		ArrayList<int[]> solutionsAleatoiresInitiales = initSolsInitiales(10);
		System.out.println("OK");
		System.out.print("choix de la solution voisine... ");
		int[] solutionVoisineInitiale = solutionsAleatoiresInitiales.get(r.nextInt(solutionsAleatoiresInitiales.size()));
		System.out.println("OK");
		System.out.print("calcul de la moyenne du delta initiale... ");
		double delta = moyenne(ws, solutionsAleatoiresInitiales, solutionVoisineInitiale);
		System.out.println("OK (delta = " + delta + ")");
		double temperature = delta / Math.log(0.2);
		System.out.println("temperature initiale = " + temperature);
		ArrayList<int[]> voisins = null;
		int[] s = solutionVoisineInitiale;
		int[] ss = null;
		int palierAccepte = 0;
		int palierTente = 0;
		int limitePalierAccepte = 12 * 1;
		int limitePalierTente = 100 * 1;
		int incrSuccessif = 0;
		double ALPHA = 0.9;

		System.out.println("on entre dans l'algo:");
		/* algo */
		do {
			System.out.println("~~~~~~~~~~~~~ PALIER TENTé = " + palierTente + ", incrSuccessif= " + incrSuccessif);
			System.out.println("on genere les voisins de " + matString(s) + ":");
			/* on genere les voisins de s */
			voisins = genererVoisins(s);
			/* on prend un des voisins au hasard */
			ss = voisins.get(r.nextInt(voisins.size()));
			System.out.println("on selectionne au hasard le voisin " + matString(ss));
			/* on calcule le delta */
			delta = calculDelta(ws, ss, s);
			palierTente++;
			System.out.println("on calcule le nouveau delta= " + delta + ", palier tenté=" + palierTente);
			if (delta > 0) {
				s = ss;
				palierAccepte++;
				incrSuccessif = 0;
				System.out.println(matString(s) + " est une bonne solution (delta=" + delta + "> 0), palier accepté=" + palierAccepte);
			}
			else {
				if (r.nextDouble() < Math.exp(delta / temperature)) {
					s = ss;
					palierAccepte++;
					incrSuccessif = 0;
					System.out.println(matString(s) +
							" est une bonne solution (u < " +
							Math.exp(delta / temperature) +
							"), palier accepté=" + palierAccepte);
				}
				else {
					System.out.println("pas bon, on va essayer un autre voisin !");
					incrSuccessif++;
				}
			}
			if (palierAccepte > limitePalierAccepte) {
				palierAccepte = 0;
				temperature = ALPHA * temperature;
				System.out.println("on a trouvé assez de bonnes solutions, on va refroidir un peu");
			}
			if (palierTente > limitePalierTente) {
				palierTente = 0;
				incrSuccessif++;
				temperature = ALPHA * temperature;
				System.out.println("on a atteint notre nombre d'essais, on va refroifir un peu");
			}
		}
		while(incrSuccessif < 3);

		/* on retourne la matrice gagnante et son score */
		System.out.println("Matrice gagnante: " + matString(s));
		int score = 0;
		try {
			ws.command("set preferenceList " + matString(s));
			ws.command("runExperiences");
			score = ((Double) ws.report("listeResultats")).intValue();
			ws.dispose();
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		System.out.println("avec un score de " + score + " morts :D");

	}

	/* calcul de la moyenne de delta initial */
	private static double moyenne(HeadlessWorkspace ws,
			ArrayList<int[]> sols,
			int[] voisinInitiale) {
		double resultat = 0;
		double solIntermediaire = 0;
		double solVoisinInitiale = 0;
		try {
			ws.command("set preferenceList " + matString(voisinInitiale));
			ws.command("runExperiences");
			solVoisinInitiale = (double) ws.report("listeResultats");
			for (int[] s : sols) {
				ws.command("set preferenceList " + matString(s));
				ws.command("runExperiences");
				solIntermediaire = (double) ws.report("listeResultats");
				resultat += solVoisinInitiale - solIntermediaire;
			}
		}
		catch(Exception e ) {
			e.printStackTrace();
		}
		resultat = resultat / sols.size();
		return resultat;
	}

	private static double calculDelta(HeadlessWorkspace ws, int[] sVoisin, int[] ss) {
		double voisin = 0;
		double tmp = 0;
		try {
			ws.command("set preferenceList " + matString(sVoisin));
			ws.command("runExperiences");
			voisin = (double) ws.report("listeResultats");
			ws.command("set preferenceList " + matString(ss));
			for (int i = 0; i < 10; i++) {
				ws.command("runExperiences");
				tmp += (double) ws.report("listeResultats");
			}
			tmp = tmp / 10;
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		return (voisin - tmp);
	}

	/* genere nb solutions aleatoires */
	private static ArrayList<int[]> initSolsInitiales(int nb) {
		ArrayList<int[]> resultat = new ArrayList<int[]>();
		for (int i = 0; i < nb; i++) {
			resultat.add(randomMat(60));
		}
		return resultat;
	}


	private static int[] randomMat(int m) {
		int[] matrice = new int[9];
		Random r = new Random();
		int somme_m = m;
		int valeur_r = 0;
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 2 ; j++) {
				valeur_r = r.nextInt(somme_m);
				matrice[(i * 3) + j] = valeur_r;
				somme_m = somme_m - valeur_r;
			}
			matrice[(i * 3) + 2] = somme_m;
			somme_m = m;
		}
		return matrice;
	}

	private static ArrayList<int[]> genererVoisins(int[] s) {
		ArrayList<int[]> voisins = new ArrayList<int[]>();
		voisins.add(varie(s, 4, 0));
		voisins.add(varie(s, 4, 1));
		voisins.add(varie(s, 4, 2));
		return voisins;
	}

	private static int[] varie(int[] m, int delta, int ligne) {
		int[] res = m.clone();
		int newA1, newA2, newA3;
		if (ligne == 0) {
			newA1 = m[ligne * 3] + delta;
			if (newA1 < 0 || newA1 > 60) {
				return m;
			}
			newA2 = m[(ligne * 3) + 1] - (delta / 2);
			if (newA2 < 0 || newA2 > 60) {
				return m;
			}
			newA3 = m[(ligne * 3) + 2] - (delta / 2);
			if (newA3 < 0 || newA3 > 60) {
				return m;
			}
		}
		else {
			if (ligne == 1) {
				newA1 = m[ligne * 3] - (delta / 2);
				if (newA1 < 0 || newA1 > 60) {
					return m;
				}
				newA2 = m[(ligne * 3) + 1] + delta;
				if (newA2 < 0 || newA2 > 60) {
					return m;
				}
				newA3 = m[(ligne * 3) + 2] - (delta / 2);
				if (newA3 < 0 || newA3 > 60) {
					return m;
				}
			}
			else {
				newA1 = m[ligne * 3] - (delta / 2);
				if (newA1 < 0 || newA1 > 60) {
					return m;
				}
				newA2 = m[(ligne * 3) + 1] - (delta / 2);
				if (newA2 < 0 || newA2 > 60) {
					return m;
				}
				newA3 = m[(ligne * 3) + 2] + delta;
				if (newA3 < 0 || newA3 > 60) {
					return m;
				}
			}
		}
		res[ligne * 3] = newA1;
		res[(ligne * 3) + 1] = newA2;
		res[(ligne * 3) + 2] = newA3;
		return res;
	}

	private static String matString(int[] m) {
		return "[ " + m[0] + " " + m[1] + " " + m[2] + " " +
				+ m[3] + " " + m[4] + " " + m[5] + " " +
				+ m[6] + " " + m[7] + " " + m[8] + " ]";
	}

}